package com.yygo.webapp.controller;

import com.yygo.Constants;
import com.yygo.jobs.SettlementTaskFactory;
import com.yygo.model.LabelValue;
import com.yygo.model.LoanProduct;
import com.yygo.model.SettlementTask;
import com.yygo.model.User;
import com.yygo.service.BankManager;
import com.yygo.service.RoleManager;
import org.apache.commons.lang.StringUtils;
import com.yygo.model.Bank;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

@Controller
@RequestMapping("/bankform*")
public class BankFormController extends BaseFormController {
    private BankManager bankManager = null;

    @Autowired
    public void setBankManager(@Qualifier("bankManager") BankManager bankManager) {
        this.bankManager = bankManager;
    }

    @Autowired
    private RoleManager roleManager;

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    public BankFormController() {
        setCancelView("redirect:banks");
        setSuccessView("redirect:banks");
    }

    @ModelAttribute
    @RequestMapping(method = RequestMethod.GET)
    protected Bank showForm(HttpServletRequest request)
    throws Exception {
        String id = request.getParameter("id");

        if (!StringUtils.isBlank(id)) {
            return bankManager.get(new Long(id));
        }

        return new Bank();
    }

    @RequestMapping(method = RequestMethod.POST)
    public String onSubmit(Bank bank, BindingResult errors, HttpServletRequest request,
                           HttpServletResponse response)
    throws Exception {
        String taskUpdate = "";
        if (request.getParameter("cancel") != null) {
            return getCancelView();
        }

        if (validator != null) { // validator is null during testing
            validator.validate(bank, errors);

            if (errors.hasErrors() && request.getParameter("delete") == null) { // don't validate when deleting
                return "bankform";
            }
        }

        log.debug("entering 'onSubmit' method...");

        boolean isNew = (bank.getId() == null);
        String success = getSuccessView();
        Locale locale = request.getLocale();

        if (request.getParameter("delete") != null) {
            bank = bankManager.get(bank.getId());
            if (bank.getSettlementTask()!=null) {
                taskUpdate = "delete";
            }
            bank.setDeleted(true);
            bankManager.save(bank);
            saveMessage(request, getText("bank.deleted", locale));
        } else {
            User user = bank.getBankUser();
            User nameUser = getUserManager().getUserByUsername(user.getUsername());
            User emailUser = getUserManager().getUserByEmail(user.getEmail());
            if (nameUser!=null && nameUser.getId()!=user.getId()){
                errors.rejectValue("bankUser.username", "errors.username.exist", new String[]{user.getUsername()}, "username is exist");
            }
            if (emailUser!=null && emailUser.getId()!=user.getId()){
                errors.rejectValue("bankUser.email", "errors.email.exist", new String[]{user.getEmail()}, "email is exist");
            }
            if (errors.hasErrors()) {
                copyErrors(request, errors);
                return "bankform";
            }
            user.setEnabled(true);
            user.getRoles().clear();
            user.addRole(roleManager.getRole(Constants.Bank_USER_ROLE));
            user.addRole(roleManager.getRole(Constants.Bank_ADMIN_ROLE));
            User adminUser = getUserManager().saveUser(user);
            bank.setBankUser(adminUser);
            bank.getUsers().add(adminUser);
            taskUpdate = updateBankTask(bank, isNew);
            bank = bankManager.save(bank);
            adminUser.setBank(bank);
            getUserManager().save(adminUser);
            String key = (isNew) ? "bank.added" : "bank.updated";
            saveMessage(request, getText(key, locale));
            if (!isNew) {
                success = "bankform";
            }
        }
        if (!taskUpdate.equals("")){
            SettlementTask task = bank.getSettlementTask();
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobKey jobKey = JobKey.jobKey("task_" + task.getId(),
                    "group_" + task.getId());
            if (taskUpdate.equals("new")){
                TriggerKey triggerKey = TriggerKey.triggerKey(
                        "task_" + task.getId(), "group_" + task.getId());
                CronTrigger trigger = (CronTrigger) scheduler
                        .getTrigger(triggerKey);
                if (null == trigger) {
                    JobDetail jobDetail = JobBuilder
                            .newJob(SettlementTaskFactory.class)
                            .withIdentity("task_" + task.getId(),
                                    "group_" + task.getId()).build();
                    jobDetail.getJobDataMap().put("scheduleJob", task);
                    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
                            .cronSchedule(task.getCronExpression());
                    trigger = TriggerBuilder
                            .newTrigger()
                            .withIdentity("task_" + task.getId(),
                                    "group_" + task.getId())
                            .withSchedule(scheduleBuilder).build();
                    scheduler.scheduleJob(jobDetail, trigger);
                }
            }
            else if (taskUpdate.equals("update")){
                TriggerKey triggerKey = TriggerKey.triggerKey("task_" + task.getId(),
                        "group_" + task.getId());
                CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCronExpression());
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                        .withSchedule(scheduleBuilder).build();
                scheduler.rescheduleJob(triggerKey, trigger);
            }
            else if (taskUpdate.equals("delete")){
                scheduler.deleteJob(jobKey);
            }
        }
        return success;
    }

    private void copyErrors(HttpServletRequest request, BindingResult errors) {
        List<ObjectError> errorMessages = errors.getAllErrors();
        for (ObjectError err : errorMessages) {
            saveError(request, getText(err.getCode(), err.getArguments(), request.getLocale()));
        }
    }

    private String updateBankTask(Bank bank, boolean isNew){
        String taskUpdate = "";
        if (isNew ){
            SettlementTask task = new SettlementTask();
            task.setBank(bank);
            task.setCronExpression("0 0 0 "+ bank.getCheckDate() +" * ?");
            bank.setSettlementTask(task);
            taskUpdate="new";
        }
        else {
            Bank bankOld = bankManager.get(bank.getId());
            int day = bank.getCheckDate();
            String cron = "0 0 0 "+ String.valueOf(day) +" * ?";
            String oldCron = bankOld.getSettlementTask().getCronExpression();
            if( !oldCron.equals(cron)){
                bankOld.getSettlementTask().setCronExpression("0 0 0 "+ bank.getCheckDate() +" * ?");
                bank.setSettlementTask(bankOld.getSettlementTask());
                taskUpdate = "update";
            }
        }
        return taskUpdate;
    }

    @ModelAttribute
    protected Set<LoanProduct> getLoanProducts(HttpServletRequest request)
            throws Exception {
        String id = request.getParameter("id");

        if (!StringUtils.isBlank(id)) {
            return bankManager.getLoanProductsByBank(new Long(id));
        }
        return new HashSet<>();
    }

    @ModelAttribute("availableDates")
    protected List<LabelValue> loadAvailableDates() {
        return getNormalList(Constants.DayInMonth);
    }
}
